.section .text, "ax"

/**
 * thread cut-in implementation
 * @prototype: void mx_thread_switch (mx_void_pt from, mx_void_pt to);
 */
.global mx_thread_switch
.type mx_thread_switch, %function
mx_thread_switch:
    /* save context of from thread */
    // stp x1, x0, [x0, #-16]!     /* x1 x0 don't have to save */
    sub x0, x0, #8
    stp x3, x2, [x0, #-16]!
    stp x5, x4, [x0, #-16]!
    stp x7, x6, [x0, #-16]!
    stp x9, x8, [x0, #-16]!
    stp x11, x10, [x0, #-16]!
    stp x13, x12, [x0, #-16]!
    stp x15, x14, [x0, #-16]!
    stp x17, x16, [x0, #-16]!
    stp x19, x18, [x0, #-16]!
    stp x21, x20, [x0, #-16]!
    stp x23, x22, [x0, #-16]!
    stp x25, x24, [x0, #-16]!
    stp x27, x26, [x0, #-16]!
    stp x29, x28, [x0, #-16]!
    mov x2, sp
    stp x2, x30, [x0, #-16]!    /* sp x30 */
    mrs x2, nzcv
    mrs x3, daif
    orr x2, x2, x3
    mrs x3, currentel
    orr x2, x2, x3
    mrs x3, spsel
    orr x2, x2, x3
    stp x30, x2, [x0, #-16]     /* break-point pstate */
    /* restore context of target thread */
    sub x1, x1, #8              /* x1 x0 need to restore, may be it's save in interrupt */
    ldp x3, x2, [x1, #-16]!
    ldp x5, x4, [x1, #-16]!
    ldp x7, x6, [x1, #-16]!
    ldp x9, x8, [x1, #-16]!
    ldp x11, x10, [x1, #-16]!
    ldp x13, x12, [x1, #-16]!
    ldp x15, x14, [x1, #-16]!
    ldp x17, x16, [x1, #-16]!
    ldp x19, x18, [x1, #-16]!
    ldp x21, x20, [x1, #-16]!
    ldp x23, x22, [x1, #-16]!
    ldp x25, x24, [x1, #-16]!
    ldp x27, x26, [x1, #-16]!
    ldp x29, x28, [x1, #-16]!
    ldp x0, x30, [x1, #-16]!
    mov sp, x0
    ldr x0, [x1, #-8]           /* pstate */
    msr spsr_el1, x0
    ldr x0, [x1, #-16]          /* bread-point */
    msr elr_el1, x0
    ldp x1, x0, [x1, #(30 * 8)]
    /* 为了能够使得在中断中保存的上下文能在此进行切换，就不能使用 ret，因为 ret 需要用到 x30，
       而在中断中切换的上下文，x30 不能没有被压栈保存，因此没有办法，只能用 eret */
    eret

/* x0 = context, x1 = from, x2 = target */
.global mx_thread_switch_interrupt
.type mx_thread_switch_interrupt, %function
mx_thread_switch_interrupt:
    sub x0, x0, #8
    add x1, x1, #8
    add x2, x2, #8
    add x5, x0, #16
    /* save context of from-thread */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x1 x0 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x3 x2 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x5 x4 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x7 x6 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x9 x8 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x11 x10 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x13 x12 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x15 x14 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x17 x16 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x19 x18 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x21 x20 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x23 x22 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x25 x24 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x27 x26 */
    ldp x4, x3, [x0], #-16
    stp x4, x3, [x1, #-16]! /* x29 x28 */
    ldr x4, [x0, #8]
    stp x5, x4, [x1, #-16]! /* sp  x30 */
    mrs x6, spsr_el1
    mrs x7, elr_el1
    stp x7, x6, [x1, #-16]  /* bread-point PSTATE */

    /* restore context of target thread */
    ldp x1, x0, [x2, #-16]!
    sub x2, x2, #16
    ldp x5, x4, [x2, #-16]!
    ldp x7, x6, [x2, #-16]!
    ldp x9, x8, [x2, #-16]!
    ldp x11, x10, [x2, #-16]!
    ldp x13, x12, [x2, #-16]!
    ldp x15, x14, [x2, #-16]!
    ldp x17, x16, [x2, #-16]!
    ldp x19, x18, [x2, #-16]!
    ldp x21, x20, [x2, #-16]!
    ldp x23, x22, [x2, #-16]!
    ldp x25, x24, [x2, #-16]!
    ldp x27, x26, [x2, #-16]!
    ldp x29, x28, [x2, #-16]!
    ldr x30, [x2, #-8]
    ldr x3, [x2, #-16]  /* sp */
    mov sp, x3
    ldr x3, [x2, #-24]  /* pstate */
    msr spsr_el1, x3
    ldr x3, [x2, #-32]  /* break-point */
    msr elr_el1, x3
    ldp x3, x2, [x2, #(26 * 8)]

    eret
